{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 文本相似度 2 多文本相似度"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 导入相关包"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from transformers import TrainingArguments, Trainer # 导入trainer相关的包\n",
    "from transformers import AutoTokenizer, AutoModelForSequenceClassification\n",
    "from datasets import load_dataset\n",
    "import os"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 加载数据集合"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dataset_dir = \"/data/datasets/SimCLUE/datasets/train_pair_1w.json\"\n",
    "dataset = load_dataset('json', data_files=dataset_dir,split='train')\n",
    "print(len(dataset))\n",
    "sum = 0\n",
    "for i in dataset:\n",
    "    if int(i[\"label\"]) == 1:\n",
    "        sum +=1 \n",
    "print(sum)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 划分数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "datasets = dataset.train_test_split(test_size=0.2)\n",
    "datasets"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建dataloader\n",
    "import torch\n",
    "tokenizer = AutoTokenizer.from_pretrained('/data/models/huggingface/chinese-macbert-base')\n",
    "def process_function(examples):\n",
    "    tokenizer_examples = tokenizer(examples['sentence1'],examples['sentence2'], truncation=True, max_length=128)\n",
    "    tokenizer_examples[\"labels\"] = [float(label) for label in examples['label']]\n",
    "    return tokenizer_examples\n",
    "\n",
    "tokenizer_datasets = datasets.map(process_function, batched=True,remove_columns=datasets['train'].column_names)\n",
    "print(tokenizer_datasets['train'][0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 创建模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 这里注意如果num_labels一旦为1 则不会计算交叉熵而是计算均方误差,任务转为回归任务\n",
    "model =  AutoModelForSequenceClassification.from_pretrained('/data/models/huggingface/chinese-macbert-base',num_labels=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 评估部分重写evaluate的部分\n",
    "#### 1、在huggingface.co 网站中找tasks 然后在看支持的评估指标\n",
    "#### 2、直接下载失败，可以github上下载源代码，然后直接加载文件夹。\n",
    "#### 3、使用 add_batch 和 最终的compute方法最后实现评估指标的汇总工作\n",
    "#### 4、可以画出雷达图"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import evaluate\n",
    "# 4 项目都加入\n",
    "acc_metric =  evaluate.load('/evaluate-main/metrics/accuracy')\n",
    "f1_metric =  evaluate.load('/evaluate-main/metrics/f1')\n",
    "precision_metric =  evaluate.load('/evaluate-main/metrics/precision')\n",
    "recall_metric =  evaluate.load('/evaluate-main/metrics/recall')\n",
    "\n",
    "# 写一个多种标准合成的评价函数\n",
    "def eval_metrics(eval_predict):\n",
    "    predictions,labels =  eval_predict\n",
    "    predictions  = [ int(p>0.5) for p in predictions] \n",
    "    labels = [ int(l) for l in labels] \n",
    "    \n",
    "    combine_acc = acc_metric.compute(predictions=predictions, references=labels)\n",
    "    f1 = f1_metric.compute(predictions=predictions, references=labels)\n",
    "    combine_acc.update(f1)\n",
    "    return combine_acc"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 训练和验证"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 创建 TrainingArguments"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "train_args =  TrainingArguments(\n",
    "    output_dir='D:/code/logs/trainer/checkpoints', # 训练模型的输出路径\n",
    "    per_device_train_batch_size=32, # 训练集 batch size\n",
    "    per_device_eval_batch_size=32, # 验证集 batch size\n",
    "    num_train_epochs=5, # 训练的 epochs 数    \n",
    "    logging_steps=150, # 每 150 个 batch 记录一次日志\n",
    "    # save_steps=100, # 每 100 个 batch 保存一次模型 和每个epoch 保存一次模型互斥\n",
    "    save_strategy = 'epoch', # 每 1 个 epoch 保存一次模型\n",
    "    save_total_limit=3, # 保存 1 个模型\n",
    "    evaluation_strategy='epoch', # 每 1 个 epoch 验证一次模型\n",
    "    learning_rate=4e-5, # 学习率\n",
    "    weight_decay=0.01, # 权重衰减\n",
    "    metric_for_best_model='f1',\n",
    "    load_best_model_at_end=True, # 加载最优模型\n",
    "    report_to='tensorboard', # 训练日志保存到 tensorboard\n",
    "    )\n",
    "train_args"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 创建  Trainer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from transformers import DataCollatorWithPadding\n",
    "\n",
    "trainer = Trainer(\n",
    "    model=model, # 指定模型\n",
    "    args=train_args, # 执行训练参数\n",
    "    train_dataset=tokenizer_datasets['train'], # 训练数据集指定 切分后的数据集\n",
    "    eval_dataset=tokenizer_datasets['test'], # 验证数据集指定\n",
    "    data_collator=DataCollatorWithPadding(tokenizer=tokenizer), # 数据集的预处理对数据集进行padding操作\n",
    "    compute_metrics = eval_metrics, # tokenizer\n",
    "    )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 模型训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "trainer.train()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 模型评估"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "trainer.evaluate()\n",
    "trainer.evaluate(tokenizer_datasets['test'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 模型预测"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "trainer.predict(tokenizer_datasets['test'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from transformers import pipeline\n",
    "model.config.id2label = {0: '不相似', 1: '相似'}\n",
    "pipe = pipeline('text-classification', model=model,tokenizer=tokenizer,device=0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "res  = pipe({\n",
    "    \"text\":\"我喜欢北京，这不仅仅是因为我是一个北京人，住在北京.而是因为北京有着悠久的历史和人文景观，还有它的生活节奏和生活氛围我都喜欢。\",\n",
    "    \"text_pair\":\"我是一个北京人，所以我喜欢这里。\"\n",
    "},function_to_apply=\"none\")\n",
    "res[\"label\"] = \"相似\" if res[\"score\"] > 0.5 else \"不相似\"\n",
    "print(res)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.19"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
